/******************************************************************************
	Cobalt - utility libraries for building XNA games
	Copyright (C) 2008 Philip Djonov

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
	for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the
	
		Free Software Foundation, Inc.
		51 Franklin Street, Fifth Floor
		Boston, MA  02110-1301, USA.
******************************************************************************/

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Content.Pipeline;

namespace Cobalt.Content.Pipeline
{

	public sealed class ContentLineReader : IDisposable
	{
		private int baseLine = 0;
		private int currentLine = 0;
		private ContentIdentity sourceIdentity;
		private TextReader reader;

		public ContentLineReader( string text, ContentIdentity sourceIdentity )
		{
			this.reader = new StringReader( text );

			if( sourceIdentity != null )
			{
				if( int.TryParse( sourceIdentity.FragmentIdentifier, out baseLine ) )
				{
					baseLine = Math.Max( baseLine - 1, 0 );
				}
				else
				{
					baseLine = 0;
				}

				this.sourceIdentity = new ContentIdentity( sourceIdentity.SourceFilename, sourceIdentity.SourceTool, (baseLine + 1).ToString() );
			}
			else
			{
				baseLine = 0;
				this.sourceIdentity = new ContentIdentity( "[Unknown File]", string.Empty, (baseLine + 1).ToString() );
			}
		}

		public static ContentLineReader FromFile( string filename )
		{
			return new ContentLineReader( File.ReadAllText( filename ),
				new ContentIdentity( filename, string.Empty, string.Empty ) );
		}

		private string GetLine()
		{
			currentLine++;
			return reader.ReadLine();
		}

		private string StripSlashSlashComment( string line, int startIndex )
		{
			return line.Substring( 0, startIndex );
		}

		private bool inSlashStarComment = false;
		private string StripSlashStarComment( string line, int startIndex )
		{
			int endIdx = line.IndexOf( "*/", startIndex >= 0 ? startIndex + 2 : 0 );
			if( endIdx != -1 )
			{
				inSlashStarComment = false;
				int begIdx = startIndex >= 0 ? startIndex : 0;
				return line.Remove( begIdx, endIdx + 2 - begIdx );
			}

			//did not find end of comment, must still be in it
			inSlashStarComment = true;
			return string.Empty;
		}

		private string StripComment( string line )
		{
			for( ; ; )
			{
				if( inSlashStarComment )
					return StripSlashStarComment( line, -1 );

				int slashIdx = line.IndexOf( "//" );
				int starIdx = line.IndexOf( "/*" );

				if( slashIdx != -1 && starIdx != -1 )
				{
					if( slashIdx < starIdx )
						return StripSlashSlashComment( line, slashIdx );
					else
						line = StripSlashStarComment( line, starIdx );
				}
				else if( slashIdx != -1 )
					return StripSlashSlashComment( line, slashIdx );
				else if( starIdx != -1 )
					line = StripSlashStarComment( line, starIdx );
				else
					//no comment
					return line;
			}
		}

		private string lastLine;
		public string LastLine
		{
			get { return lastLine; }
		}

		public ContentIdentity SourceIdentity { get { return sourceIdentity; } }

		public ContentIdentity LastLineIdentity
		{
			get
			{
				return new ContentIdentity( sourceIdentity.SourceFilename,
					sourceIdentity.SourceTool, (baseLine + currentLine + 1).ToString() );
			}
		}

		private string InternalReadLine()
		{
			for( ; ; )
			{
				string line = GetLine();
				if( line == null )
					return null;

				line = StripComment( line );
				line = line.Trim();

				if( line == string.Empty )
					continue;

				return line;
			}
		}

		public string ReadLine()
		{
			lastLine = InternalReadLine();
			return lastLine;
		}

		public string ReadRequiredLine()
		{
			string line = ReadLine();

			if( line == null )
				throw new InvalidContentException( "Unexpected end of file.", LastLineIdentity );

			return line;
		}

		public string ReadExactLine( string expectedValue, StringComparison cmp )
		{
			string line = ReadLine();

			if( line == null )
			{
				if( expectedValue != null )
					throw new InvalidContentException( "Unexpected end of file.", LastLineIdentity );

				return null;
			}

			if( string.Compare( line, expectedValue, cmp ) != 0 )
				throw new InvalidContentException( "Unexpected token.", LastLineIdentity );

			return line;
		}

		public string ReadExactLine( string expectedValue )
		{
			return ReadExactLine( expectedValue, StringComparison.Ordinal );
		}

		#region IDisposable Members

		public void Dispose()
		{
			reader.Dispose();
		}

		#endregion
	}

}